{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 张量\n",
    "顾名思义，tensorflow 是一个用来定义和计算包含张量的框架，张量是将向量和矩阵推向更高维度的一种方法的数据表现，在内部，tensorflow 将张量表示为基础数据类型的 n 维数组。\n",
    "\n",
    "当你在写一个 tensorflow 程序时，传递和操作的主要对象是 [tf.Tensor](https://tensorflow.google.cn/api_docs/python/tf/Tensor)。tf.Tensor 对象表示一个定义的计算，它最终会产生一个值。tensorflow 的工作原理是，首先构建一个 tf.Tensor 对象图，详细说明如何基于其它可用的张量计算每个张量，然后运行该图的一部分以获取所需的结果。\n",
    "\n",
    "一个 tf.Tensor 有以下属性：\n",
    "1. 数据类型（像float32、int32、string）\n",
    "2. 形状\n",
    "\n",
    "张量的每个元素的数据类型都是一样的，并且已知。它的形状（维度以及每个维数的大小）可能是潜在的。在已知输入张量的形状的情况下，大多数操作提供已知形状的输出，但在某些情况下，只有在图形执行的手才能得到张量的形状。\n",
    "\n",
    "有一些张量是特殊的，这些将在张量导向器的其他单元中讨论。主要有：\n",
    "- [tf.Variable](https://tensorflow.google.cn/api_docs/python/tf/Variable)\n",
    "- [tf.constant](https://tensorflow.google.cn/api_docs/python/tf/constant)\n",
    "- tf.placeholder\n",
    "- [tf.SparseTensor](https://tensorflow.google.cn/api_docs/python/tf/sparse/SparseTensor)\n",
    "\n",
    "除了 tf.Variable，张量的值是不可变的，这意味着在同一个环境的上下文中张量有唯一值。然而，执行同一个张量两次可能会得到不同的值，比如从磁盘中读取获取，或者随机生成数字、\n",
    "\n",
    "## 秩\n",
    "tf.Tensor 的秩是它们的数据的维度，注意张量的秩与数学中矩阵的秩不同，数学中的更加学术。如下表所示，tensorflow 中的每一个秩对于于数学中的不同实体：\n",
    "\n",
    "| 秩 | 数学实体 |\n",
    "| - | - |\n",
    "| 0 | 标量 |\n",
    "| 1 | 向量 |\n",
    "| 2 | 矩阵 |\n",
    "| 3 | 3张量 |\n",
    "| n | n张量 |\n",
    "\n",
    "### 秩为0\n",
    "下面的代码段演示如何创建几个秩为 0 的变量：\n",
    "```python\n",
    "mammal = tf.Variable(\"Elephant\", tf.string)\n",
    "ignition = tf.Variable(451, tf.int16)\n",
    "floating = tf.Variable(3.14159265359, tf.float64)\n",
    "its_complicated = tf.Variable(12.3 - 4.85j, tf.complex64)\n",
    "```\n",
    "> 注意：一个字符串在 tensorflow 中被看作一个对象，而不是像字符的序列。它可能是标量字符串、向量字符串等等\n",
    "\n",
    "### 秩为1\n",
    "要创建秩为 1的 tf.Tensor 对象，可以传递一个列表作为初始值。例如：\n",
    "```python\n",
    "mystr = tf.Variable([\"Hello\"], tf.string)\n",
    "cool_numbers  = tf.Variable([3.14159, 2.71828], tf.float32)\n",
    "first_primes = tf.Variable([2, 3, 5, 7, 11], tf.int32)\n",
    "its_very_complicated = tf.Variable([12.3 - 4.85j, 7.5 - 6.23j], tf.complex64)\n",
    "```\n",
    "\n",
    "### 高秩\n",
    "一个秩为 2 的 tf.Tensor 对象由至少一行和至少一列组成：\n",
    "```python\n",
    "mymat = tf.Variable([[7],[11]], tf.int16)\n",
    "myxor = tf.Variable([[False, True],[True, False]], tf.bool)\n",
    "linear_squares = tf.Variable([[4], [9], [16], [25]], tf.int32)\n",
    "squarish_squares = tf.Variable([ [4, 9], [16, 25] ], tf.int32)\n",
    "rank_of_squares = tf.rank(squarish_squares)\n",
    "mymatC = tf.Variable([[7],[11]], tf.int32)\n",
    "```\n",
    "高秩张量类似由 n 维数组组成。比如，在图像处理过程中，使用了许多秩为 4 的张量，其尺寸对应于批处理中的批次、图像高度、图像宽度和颜色通道：\n",
    "```python\n",
    "my_image = tf.zeros([10, 299, 299, 3])  # 批次 x 高度 x 宽度 x 颜色通道\n",
    "```\n",
    "\n",
    "### 获取 tf.Tensor 对象的秩\n",
    "为了获取 tf.Tensor 的秩，可以调用 [tf.rank](https://tensorflow.google.cn/api_docs/python/tf/rank) 方法。例如，以下方法以编程方式确定上一节中定义的 tf.Tensor 的秩：\n",
    "```python\n",
    "r = tf.rank(my_image)\n",
    "# 在图运行完后，r 的值为 4\n",
    "```\n",
    "\n",
    "### 指向 tf.Tensor 切片\n",
    "当 tf.Tensor 是一个 n 维数数组的单元，你需要指定n个索引才能要访问tf.Tensor中的单个单元格。\n",
    "\n",
    "对于秩为 0 的张量（标量），没有索引，它已经是一个单独的数字。\n",
    "\n",
    "对于秩为 1 的张量（向量），需要传入一个下标你才能访问数据：\n",
    "```python\n",
    "my_scalar = my_vector[2]\n",
    "```\n",
    "注意:如果你想从向量中动态选择一个元素的话，[ ] 内部传递的索引本身可以是 tf.Tensor 标量。\n",
    "\n",
    "如果秩为 2 或者更高，位置定位就很有趣。对于秩为 2 的 tf.Tensor，需要传入两个数字并返回一个标量：\n",
    "```python\n",
    "my_scalar = my_matrix[1, 2]\n",
    "```\n",
    "然而，传入一个数字就会返回一个矩阵的子向量，如下所示：\n",
    "```python\n",
    "my_row_vector = my_matrix[2]\n",
    "my_column_vector = my_matrix[:, 3]\n",
    "```\n",
    "这个 `:` 符号是 python 的切片语法，表示 \"不要管这个维度\"，这在高阶张量中很有用，因为它允许你访问其子向量、子矩阵，甚至其它的子传感器。\n",
    "\n",
    "## 形状\n",
    "形状是张量里每个元素的维度。tensorflow 在图构建的时候自动推导形状，自动推导的形状可能有已知或未知的秩。如果秩是已知的，则每个维度的大小可能已知或未知。\n",
    "\n",
    "tensorflow 文档使用 3 个符号来描述张量维度：秩、形状、维数。下面的表格显示了三者之间的关系：\n",
    "\n",
    "| 秩 | 形状 | 维度 | 例子 |\n",
    "| - | - | - | - |\n",
    "| 0 | [] | 0-D | 为标量的一个 0-D 张量 |\n",
    "| 1 | [D0] | 1-D | 形状为 [5] 的一个 1-D 张量 |\n",
    "| 2 | [D0, D1] | 2-D | 形状为 [3, 4] 的一个 2-D 张量 |\n",
    "| 3 | [D0, D1, D2] | 3-D | 形状为 [1, 4, 3] 的一个 3-D 张量 |\n",
    "| n | [D0, D1, ..., Dn-1] | n-D | 形状为 [D0, D1, ..., Dn-1] 的一个张量 |\n",
    "形状可以通过 python 列表或者 int元组来表示，也可以使用 [tf.TensorShape](https://tensorflow.google.cn/api_docs/python/tf/TensorShape) 来表示。\n",
    "\n",
    "### 获取 tf.Tensor 形状\n",
    "有两种方法获取 tf.Tensor 的形状。当创建图的时候，经常需要询问关于张量形状的已知信息。能够通过读取 tf.Tensor 对象的 shape 属性获取，此方法返回一个 TensorShape 对象，这是表示部分指定形状的一种方便方法（因为在构建图形时，并非所有形状都是完全已知的）。\n",
    "\n",
    "也可以得到一个 tf.Tensor，它将在运行时表示另一个 tf.Tensor 的完全定义形状。这是通过 tf.shape 操作完成的，这样，你可以通过构建依赖输入的 tf.Tensor 动态形状的其它张量来创建一个图来操作张量的形状。\n",
    "\n",
    "例如，下面是如何使 0 向量的大小与给定矩阵中的列数相同：\n",
    "```python\n",
    "zeros = tf.zeros(my_matrix.shape[1])\n",
    "```\n",
    "\n",
    "### 改变 tf.Tensor 形状\n",
    "张量的元素数是其所有形状大小的乘积。标量的元素数始终为 1。由于通常有许多不同的形状具有相同数量的元素，所以通常可以方便地改变 tf.Tensor 的形状，使其元素保持固定。这可以通过 [tf.reforme](https://tensorflow.google.cn/api_docs/python/tf/reshape) 完成。\n",
    "\n",
    "下面的例子展示了如何重新塑造张量的形状：\n",
    "```python\n",
    "rank_three_tensor = tf.ones([3, 4, 5])\n",
    "matrix = tf.reshape(rank_three_tensor, [6, 10])  # 将现有数据重新塑造为一个 6x10 矩阵\n",
    "matrixB = tf.reshape(matrix, [3, -1])  #  将现有内容重塑为一个 3x20 矩阵。-1 表示计算该维度的大小。\n",
    "matrixAlt = tf.reshape(matrixB, [4, 3, -1])  # 将现有的内容重塑为一个 4x3x5 的张量\n",
    "\n",
    "# 注意，经过更改形状的张量的元素数必须与原始的元素数匹配。因此，下面的示例将生成一个错误，因为最后一个维度的可能值与元素的数量不匹配。\n",
    "yet_another = tf.reshape(matrixAlt, [13, 2, -1])\n",
    "```\n",
    "\n",
    "## 数据类型\n",
    "除了维度之外，张量还有数据类型。要获取数据类型的完整列表，参考 [tf.DType](https://tensorflow.google.cn/api_docs/python/tf/dtypes/DType)。\n",
    "\n",
    "不可能有一个具有多个数据类型的 tf.Tensor。但是，可以将任意数据结构序列化为字符串并将其存储在tf.Tensors 中。\n",
    "\n",
    "可以使用 [tf.cast](https://tensorflow.google.cn/api_docs/python/tf/Tensor) 将 tf.Tensor 从一个数据类型转换到另一个数据类型：\n",
    "```python\n",
    "# 把一个常整数张量转换成浮点数。\n",
    "float_tensor = tf.cast(tf.constant([1, 2, 3]), dtype=tf.float32)\n",
    "```\n",
    "\n",
    "可以使用 Tensor.dtype 属性检查 tf.Tensor 的数据类型。\n",
    "\n",
    "从 python 对象创建 tf.Tensor 时，可以选择指定数据类型。否则，tensorflow 将选择一个可以表示数据的数据类型。tensorflow 将 python 整数转换为 [tf.int32](https://tensorflow.google.cn/api_docs/python/tf#int32)，将 python 浮点数转换为 [tf.float32](https://tensorflow.google.cn/api_docs/python/tf#float32)。此外，tensorflow 使用 numpy 在转换为数组时使用的相同规则。\n",
    "\n",
    "## 张量的求值\n",
    "一旦建立了计算图，就可以运行生成特定 tf.Tensor 的计算，并获取分配给它的值。这对于调试以及tensorflow 进行的大部分工作都很有用。\n",
    "\n",
    "计算张量的最简单方法是使用 [Tensor.eval](https://tensorflow.google.cn/api_docs/python/tf/Tensor#eval) 方法。例如：\n",
    "```python\n",
    "constant = tf.constant([1, 2, 3])\n",
    "tensor = constant * constant\n",
    "print(tensor.eval())\n",
    "```\n",
    "\n",
    "eval 方法仅在默认 tf.Session 处于活动状态时才起作用（有关详细信息，参考 [Graphs and Sessions](https://tensorflow.google.cn/guide/graphs)）。\n",
    "\n",
    "eval 返回一个numpy数组，其内容与张量相同。\n",
    "\n",
    "有时不可能在没有上下文的情况下计算 tf.Tensor，因为它的值可能依赖于不可用的动态信息。例如，如果不为占位符提供值，则无法计算依赖于占位符的张量：\n",
    "```python\n",
    "p = tf.placeholder(tf.float32)\n",
    "t = p + 1.0\n",
    "t.eval()  # 此操作将失败，因为占位符没有得到值。\n",
    "t.eval(feed_dict={p:2.0})  # 此操作将成功，因为将一个值提供给占位符。\n",
    "```\n",
    "注意：可以输入任何 tf.Tensor，而不仅仅是占位符。\n",
    "\n",
    "其它模型的构造可能会使 tf.Tensor 的计算变得复杂。tensorflow 不能直接计算函数或控制流结构内部定义的tf.Tensors。如果 tf.Tenso r依赖于队列中的某个值，则仅当某个值出队后，对 tf.Tensor 求值才起作用；否则，求值操作将挂起。使用队列时，记住在计算任何 tf.tensor 之前先调用 tf.train.start_queue_runners。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6rc1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
